package com.talent.web.aspect;

import cn.hutool.crypto.digest.DigestUtil;
import com.talent.web.annotation.SubmitLimit;
import com.talent.web.enums.ResponseEnum;
import com.talent.web.model.TalentResult;
import com.talent.web.redis.RedisLockService;
import net.sf.jsqlparser.Model;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Objects;

/**
 * @ClassName：SubmitLimitAspect
 * @Author: hszhou
 * @Date: 2025/1/22 11:30
 * @Description: 必须描述类做什么事情, 实现什么功能
 */
@Component
@Aspect
@Order(1)
public class SubmitLimitAspect {

    private static final Logger log = LoggerFactory.getLogger(SubmitLimitAspect.class);


    private static final  String REDIS_SEPARATOR=":";

    private static final String DEFAULT_LOCK_VALUE="DEFAULT_SUBMIT_LOCK_VALUE";


    private final RedisLockService redisLockService;


    public SubmitLimitAspect(RedisLockService redisLockService){
        this.redisLockService = redisLockService;
    }

    @Around(value = "@annotation(com.talent.web.annotation.SubmitLimit)")
    public Object doAround(ProceedingJoinPoint joinPoint){

        HttpServletRequest request = getHttpServletRequest();
        SubmitLimit annotation = getAnnotation(joinPoint);

        String lockKey = buildKey(joinPoint, request, annotation.headers());

        boolean lock = redisLockService.tryLock(lockKey,DEFAULT_LOCK_VALUE, Duration.ofMillis(annotation.waitTime()));

        if(!lock){
            return TalentResult.error(annotation.tip());
        }
        try {
            return execute(joinPoint);
        }finally {
            redisLockService.releaseLock(lockKey,DEFAULT_LOCK_VALUE);
        }
    }


    private Object execute(ProceedingJoinPoint joinPoint){
        try {
            return joinPoint.proceed();
        } catch (Throwable e) {
            return TalentResult.error(ResponseEnum.ERROR);
        }
    }

    private HttpServletRequest getHttpServletRequest(){
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        assert attributes != null;
        return attributes.getRequest();
    }


    private SubmitLimit getAnnotation(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        return method.getAnnotation(SubmitLimit.class);
    }

    private String buildKey(JoinPoint joinPoint,HttpServletRequest request,String[] headers){

        log.debug("构建key");
        String requestHeader = getRequestHeader(request, headers);
        String requestBody = getRequestBody(joinPoint.getArgs());

        String requestParamSign = DigestUtil.sha1Hex(requestHeader + requestBody);
        return joinPoint.getSignature().getDeclaringType().getSimpleName() +
                REDIS_SEPARATOR +
                joinPoint.getSignature().getName() +
                REDIS_SEPARATOR +
                requestParamSign;
    }


    private String getRequestHeader(HttpServletRequest request,String[] customHeaders){
        if(Objects.isNull(customHeaders)){
            return "";
        }
        StringBuilder sb  = new StringBuilder();
        for (String customHeader : customHeaders) {
            sb.append(request.getHeader(customHeader));
        }
        return sb.toString();
    }

    private String getRequestBody(Object[] args){

        if(Objects.isNull(args)){
            return "";
        }

        StringBuilder sb = new StringBuilder();
        for (Object arg : args) {
            if(arg instanceof  HttpServletRequest
             || arg instanceof HttpServletResponse
            || arg instanceof MultipartFile
            || arg instanceof BindingResult
            || arg instanceof  MultipartFile[]
            || arg instanceof Model
            || arg instanceof ExtendedServletRequestDataBinder
            || arg instanceof  byte[]){
                continue;
            }
            sb.append(arg.toString());
        }
        return sb.toString();
    }
}
